package com.tyylab.kestrel.framework.spring.rpc.feign.impl;

import com.alibaba.fastjson.JSONObject;
import com.tyylab.kestrel.framework.core.build.ProxyMethodNameGenerator;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.This;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * Feign Invoke Interceptor with fallback support
 * @author tyylab
 */
@Slf4j
public class FeignInvokeInterceptor {
    
    @RuntimeType
    public static Object intercept(@Origin Method method, @This Object feignImpl, @AllArguments Object[] arguments) {
        try {
            if (!(feignImpl instanceof IFeignSupport)) {
                log.warn("FeignImpl is not instance of IFeignSupport: {}", feignImpl.getClass().getName());
                return getDefaultValue(method.getReturnType());
            }
            
            Object target = ((IFeignSupport) feignImpl).getFeign();
            if (target == null) {
                log.error("Feign target is null for method: {}", method.getName());
                return getDefaultValue(method.getReturnType());
            }
            
            String proxyMethodName = ProxyMethodNameGenerator.generateMethodName(method);
            Class<?> targetClass = target.getClass();
            Method targetMethod = targetClass.getDeclaredMethod(proxyMethodName, JSONObject.class);
            
            if (targetMethod == null) {
                log.error("Target method not found: {} in class: {}", proxyMethodName, targetClass.getName());
                return getDefaultValue(method.getReturnType());
            }
            
            // Wire JSON params
            JSONObject params = new JSONObject();
            Type[] paramTypes = method.getGenericParameterTypes();//method.getParameterTypes();
            
            if (arguments != null && arguments.length > 0) {
                for (int idx = 0; idx < arguments.length; idx++) {
                    String propertyName = ProxyMethodNameGenerator.generateMethodParamPropertyName(idx, paramTypes[idx]);
                    if (arguments[idx] != null) {
                        params.put(propertyName, arguments[idx]);
                    }
                }
            }
            
            targetMethod.setAccessible(true);
            Object result = targetMethod.invoke(target, params);
            
            log.debug("Feign call successful for method: {} with result: {}", method.getName(), result);
            return result;
            
        } catch (Exception e) {
            // Log the exception details
            log.error("Feign call failed for method: {} with arguments: {}", 
                    method.getName(), arguments, e);
            
            // Return default value based on return type
            return getDefaultValue(method.getReturnType());
        }
    }
    
    /**
     * Get default value for different return types
     * @param returnType the return type of the method
     * @return default value
     */
    private static Object getDefaultValue(Class<?> returnType) {
        if (returnType == void.class || returnType == Void.class) {
            return null;
        } else if (returnType == boolean.class || returnType == Boolean.class) {
            return false;
        } else if (returnType == char.class || returnType == Character.class) {
            return '\0';
        } else if (returnType == byte.class || returnType == Byte.class) {
            return (byte) 0;
        } else if (returnType == short.class || returnType == Short.class) {
            return (short) 0;
        } else if (returnType == int.class || returnType == Integer.class) {
            return 0;
        } else if (returnType == long.class || returnType == Long.class) {
            return 0L;
        } else if (returnType == float.class || returnType == Float.class) {
            return 0.0f;
        } else if (returnType == double.class || returnType == Double.class) {
            return 0.0;
        } else if (returnType == String.class) {
            return "";
        } else if (returnType.isArray()) {
            return java.lang.reflect.Array.newInstance(returnType.getComponentType(), 0);
        } else {
            // For complex objects, return null
            return null;
        }
    }
}
